package auth

import (
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
)

// ZHandler is a gin middleware creator that checks if the user has the required access.
// ZHandler is similar to ZMiddleware, but it is used to check the access of a single API.
func ZHandler(accesses []Access) gin.HandlerFunc {
	accessMap := make(map[Access]bool)
	for _, a := range accesses {
		accessMap[a] = true
	}
	return func(c *gin.Context) {
		permission := checkPermission(accessMap, c)
		if !permission {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}
		c.Next()
	}
}

// ZMiddleware is a gin middleware creator that checks if the user has the required access.
// ZMiddleware is similar to ZHandler, but it is used to check the access of multiple APIs and supports path matching.
func ZMiddleware(accessMap map[string][]Access) gin.HandlerFunc {

	accessSearchMap := make(map[string]map[Access]bool)

	for url, accesses := range accessMap {
		m := make(map[Access]bool)
		for _, access := range accesses {
			m[access] = true
		}
		accessSearchMap[url] = m
	}

	return func(c *gin.Context) {
		url := c.Request.URL.Path
		urlPattern := strings.Split(url, "/")

		permission := false

		for i := len(urlPattern); i >= 0; i-- {
			u := strings.Join(urlPattern[:i], "/")
			if pathAccesses, exist := accessSearchMap[u]; exist {
				permission = checkPermission(pathAccesses, c)
				break
			} else {
				if i == 0 {
					permission = true
				}
				continue
			}
		}
		if !permission {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}
		c.Next()
	}
}

func checkPermission(pathAccess map[Access]bool, c *gin.Context) bool {
	if accesses, exist := c.Get(AccessesXAuthKey); exist {
		return accessOverlap(pathAccess, accesses.([]Access))
	}
	return false
}

func accessOverlap(pathAccesses map[Access]bool, userAccesses []Access) bool {
	for _, ua := range userAccesses {
		if _, exist := pathAccesses[ua]; exist {
			return true
		}
	}
	return false
}
